let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
+ let skipTopLeft = false;
+ let skipTopRight = false;
+ let skipBottomLeft = false;
+ let skipBottomRight = false;
+
+ switch (this._arrowSide) {
+ case St.Side.TOP:
+ if (this._arrowOrigin == x1)
+ skipTopLeft = true;
+ else if (this._arrowOrigin == x2)
+ skipTopRight = true;
+ break;
+
+ case St.Side.RIGHT:
+ if (this._arrowOrigin == y1)
+ skipTopRight = true;
+ else if (this._arrowOrigin == y2)
+ skipBottomRight = true;
+ break;
+
+ case St.Side.BOTTOM:
+ if (this._arrowOrigin == x1)
+ skipBottomLeft = true;
+ else if (this._arrowOrigin == x2)
+ skipBottomRight = true;
+ break;
+
+ case St.Side.LEFT:
+ if (this._arrowOrigin == y1)
+ skipTopLeft = true;
+ else if (this._arrowOrigin == y2)
+ skipBottomLeft = true;
+ break;
+ }
+
cr.moveTo(x1 + borderRadius, y1);
if (this._arrowSide == St.Side.TOP) {
- if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
- cr.lineTo(this._arrowOrigin, y1 - rise);
- cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
- } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
- cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
- cr.lineTo(this._arrowOrigin, y1 - rise);
+ if (skipTopLeft) {
+ cr.moveTo(x1, y2 - borderRadius);
+ cr.lineTo(x1, y1 - rise);
+ cr.lineTo(x1 + halfBase, y1);
+ } else if (skipTopRight) {
+ cr.lineTo(x2 - halfBase, y1);
+ cr.lineTo(x2, y1 - rise);
+ cr.lineTo(x2, y1 + borderRadius);
} else {
cr.lineTo(this._arrowOrigin - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
}
}
- cr.lineTo(x2 - borderRadius, y1);
-
- // top-right corner
- cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
- 3*Math.PI/2, Math.PI*2);
+ if (!skipTopRight) {
+ // top-right corner
+ cr.lineTo(x2 - borderRadius, y1);
+ cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
+ 3*Math.PI/2, Math.PI*2);
+ }
if (this._arrowSide == St.Side.RIGHT) {
- if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
- cr.lineTo(x2 + rise, this._arrowOrigin);
- cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
- } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
- cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
- cr.lineTo(x2 + rise, this._arrowOrigin);
+ if (skipTopRight) {
+ cr.lineTo(x2 + rise, y1);
+ cr.lineTo(x2 + rise, y1 + halfBase);
+ } else if (skipBottomRight) {
+ cr.lineTo(x2, y2 - halfBase);
+ cr.lineTo(x2 + rise, y2);
+ cr.lineTo(x2 - borderRadius, y2);
} else {
cr.lineTo(x2, this._arrowOrigin - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
}
}
- cr.lineTo(x2, y2 - borderRadius);
-
- // bottom-right corner
- cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
- 0, Math.PI/2);
+ if (!skipBottomRight) {
+ // bottom-right corner
+ cr.lineTo(x2, y2 - borderRadius);
+ cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
+ 0, Math.PI/2);
+ }
if (this._arrowSide == St.Side.BOTTOM) {
- if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
- cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
- cr.lineTo(this._arrowOrigin, y2 + rise);
- } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
- cr.lineTo(this._arrowOrigin, y2 + rise);
- cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
+ if (skipBottomLeft) {
+ cr.lineTo(x1 + halfBase, y2);
+ cr.lineTo(x1, y2 + rise);
+ cr.lineTo(x1, y2 - borderRadius);
+ } else if (skipBottomRight) {
+ cr.lineTo(x2, y2 + rise);
+ cr.lineTo(x2 - halfBase, y2);
} else {
cr.lineTo(this._arrowOrigin + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
}
}
- cr.lineTo(x1 + borderRadius, y2);
-
- // bottom-left corner
- cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
- Math.PI/2, Math.PI);
+ if (!skipBottomLeft) {
+ // bottom-left corner
+ cr.lineTo(x1 + borderRadius, y2);
+ cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
+ Math.PI/2, Math.PI);
+ }
if (this._arrowSide == St.Side.LEFT) {
- if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
- cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
- cr.lineTo(x1 - rise, this._arrowOrigin);
- } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
- cr.lineTo(x1 - rise, this._arrowOrigin);
- cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
+ if (skipTopLeft) {
+ cr.lineTo(x1, y1 + halfBase);
+ cr.lineTo(x1 - rise, y1);
+ cr.lineTo(x1 + borderRadius, y1);
+ } else if (skipBottomLeft) {
+ cr.lineTo(x1 - rise, y2)
+ cr.lineTo(x1 - rise, y2 - halfBase);
} else {
cr.lineTo(x1, this._arrowOrigin + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
}
}
- cr.lineTo(x1, y1 + borderRadius);
-
- // top-left corner
- cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
- Math.PI, 3*Math.PI/2);
+ if (!skipTopLeft) {
+ // top-left corner
+ cr.lineTo(x1, y1 + borderRadius);
+ cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
+ Math.PI, 3*Math.PI/2);
+ }
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.fillPreserve();
let arrowBase = themeNode.get_length('-arrow-base');
let borderRadius = themeNode.get_length('-arrow-border-radius');
let margin = (4 * borderRadius + borderWidth + arrowBase);
- let halfMargin = margin / 2;
- let themeNode = this.actor.get_theme_node();
+ let halfMargin = margin / 2;
let gap = themeNode.get_length('-boxpointer-gap');
let padding = themeNode.get_length('-arrow-rise');
break;
}
- // Now align and position the pointing axis, making sure
- // it fits on screen
+ // Now align and position the pointing axis, making sure it fits on
+ // screen. If the arrowOrigin is so close to the edge that the arrow
+ // will not be isosceles, we try to compensate as follows:
+ // - We skip the rounded corner and settle for a right angled arrow
+ // as shown below. See _drawBorder for further details.
+ // |\_____
+ // |
+ // |
+ // - If the arrow was going to be acute angled, we move the position
+ // of the box to maintain the arrow's accuracy.
+
+ let arrowOrigin;
+ let halfBase = Math.floor(arrowBase/2);
+ let halfBorder = borderWidth / 2;
+ let [x1, y1] = [halfBorder, halfBorder];
+ let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
+
switch (this._arrowSide) {
case St.Side.TOP:
case St.Side.BOTTOM:
resX = Math.max(resX, monitor.x + padding);
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
- this.setArrowOrigin(sourceCenterX - resX);
+
+ arrowOrigin = sourceCenterX - resX;
+ if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
+ if (arrowOrigin > x1)
+ resX += (arrowOrigin - x1);
+ arrowOrigin = x1;
+ } else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
+ if (arrowOrigin < x2)
+ resX -= (x2 - arrowOrigin);
+ arrowOrigin = x2;
+ }
break;
case St.Side.LEFT:
resY = Math.max(resY, monitor.y + padding);
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
- this.setArrowOrigin(sourceCenterY - resY);
+ arrowOrigin = sourceCenterY - resY;
+ if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
+ if (arrowOrigin > y1)
+ resY += (arrowOrigin - y1);
+ arrowOrigin = y1;
+ } else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
+ if (arrowOrigin < y2)
+ resX -= (y2 - arrowOrigin);
+ arrowOrigin = y2;
+ }
break;
}
+ this.setArrowOrigin(arrowOrigin);
+
let parent = this.actor.get_parent();
let success, x, y;
while (!success) {